
#pragma once

#include <stdio.h>

#include "core/olisp_core.h"
#include "utility/olisp_quote_mgr.h"

typedef struct
{
    FILE * err_stream;

#ifdef LISP_FEAT_QUOTE_SUPPORT
    lisp_quote_mgr_t * quote_mgr;
#endif
} interpreter_context;

/*
 * represents a message severity - used in interpreter::message function
 */
enum lisp_interpreter_message_severity_t
{
    MSG_SEV_ERROR,
    MSG_SEV_WARNING,
};

/*
 * represents an interpreter base
 */
typedef struct
{
    struct interpreter_vtbl_t * vtbl;
} interpreter_t;

/*
 * lisp function prototype
 */
typedef obj_t * (* lisp_function_proto_t)(
    interpreter_t * interp, list_obj_t * args );

typedef struct interpreter_vtbl_t interpreter_vtbl_t;
struct interpreter_vtbl_t
{
    /*
     * disposes object
     */
    void (* dispose)( interpreter_t * self );

    /*
     * sends a message to an interpreter
     * if location is non-null it may not be addrefed - this
     * function makes no release on the given location element
     */
    void (* message)( interpreter_t * self, int severity,
        obj_t * location, const char * msg );

    /*
     * interprets one another lisp element
     */
    obj_t * (* interpret)( interpreter_t * self, obj_t * elem );

    /*
     * registers a lisp function
     * returns 1 if succeeds, 0 if fails
     */
    int (* add_func)( interpreter_t * self,
        obj_t * func_name, lisp_function_proto_t proto );
};

/*
 * creates an interpreter's instance
 */
interpreter_t * create_interpreter( interpreter_context * ctx );

/*
 * helper functions
 * TODO: move this to olisp_helpers
 */
STATIC_INLINE void interpreter_dispose( interpreter_t * self )
{ self->vtbl->dispose( self ); }

STATIC_INLINE void interpreter_message( interpreter_t * self, int severity,
        obj_t * location, const char * msg )
{ self->vtbl->message( self, severity, location, msg ); }

STATIC_INLINE obj_t * interpreter_interpret( interpreter_t * self, obj_t * elem )
{ return self->vtbl->interpret( self, elem ); }

STATIC_INLINE int interpreter_add_func( interpreter_t * self,
        obj_t * func_name, lisp_function_proto_t proto )
{ return self->vtbl->add_func( self, func_name, proto ); }
